//
//  Generics.swift
//  cnswift-demo
//
//  Created by YangWei on 2021/1/7.
//  Copyright © 2021 YangWei. All rights reserved.
//

import UIKit

class Generics: SWDBaseObject {
    
    class func run() {
        let generics = Generics()
        
        // 1 泛型解决的问题
        generics.theProblemThatGenericsSolve()
        
        // 2 泛型函数
        generics.genericFunctions()
        
        // 5 泛型类型
        generics.genericTypes()
        
        // 6 泛型扩展
        generics.extendingAGenericType()
        
        // 7.2 类型约束实践
        generics.typeConstraintsInAction()
        
        // 8.1 关联类型实践
        generics.associatedTypesInAction()
        
        // 8.2 扩展现有类型来指定关联类型
        generics.extendingAnExistingTypeToSpecifyAnAssociatedType()
        
        // 8.4 在关联类型约束里使用协议
        generics.usingAProtocolInItsAssociatedTypeSConstraints()
        
        // 9 泛型 Where 语句
        generics.genericWhereClauses()
        
        // 10 具有泛型 Where 子句的扩展
        generics.extensionsWithAGenericWhereClause()
        
        // 11 包含上下文关系的 Where 分句
        generics.contextualWhereClauses()
        
        // 12 具有泛型 Where 子句的关联类型
        generics.associatedTypesWithAGenericWhereClause()
    }
    
    /// 1 泛型解决的问题
    func theProblemThatGenericsSolve() {
        let title = "1 泛型解决的问题";
        printStartLine(title)
        
        /* 调用 swapTwoInts 函数来交换两个 Int 类型变量 */
        var someInt = 3
        var anotherInt = 107
        print("someInt 交换前是 \(someInt), anotherInt 交换前是 \(anotherInt)")
        swapTwoInts(&someInt, &anotherInt)
        print("someInt 交换后是 \(someInt), anotherInt 交换后是 \(anotherInt)")
        // "someInt is now 107, and anotherInt is now 3"
        
        printEndLine(title)
    }
    
    /// 1 泛型解决的问题
    /// 函数使用输入输出参数（inout）来交换 a 和 b 的值
    func swapTwoInts(_ a: inout Int, _ b: inout Int) {
        // 将 b 的原始值换成了 a，将 a 的原始值换成了 b
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    /// 1 泛型解决的问题
    /// 函数使用输入输出参数（inout）来交换 a 和 b 的值
    func swapTwoStrings(_ a: inout String, _ b: inout String) {
        let temporaryA = a
        a = b
        b = temporaryA
    }

    /// 1 泛型解决的问题
    /// 函数使用输入输出参数（inout）来交换 a 和 b 的值
    func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    /// 2 泛型函数
    func genericFunctions() {
        let title = "2 泛型函数";
        printStartLine(title)
        
        var someInt = 3
        var anotherInt = 107
        print("someInt 交换前是 \(someInt), anotherInt 交换前是 \(anotherInt)")
        swapTwoValues(&someInt, &anotherInt)
        print("someInt 交换后是 \(someInt), anotherInt 交换后是 \(anotherInt)")
        // someInt 现在是 107，anotherInt 现在是 3
         
        var someString = "hello"
        var anotherString = "world"
        print("someString 交换前是 \(someString), anotherString 交换前是 \(anotherString)")
        swapTwoValues(&someString, &anotherString)
        print("someString 交换后是 \(someString), anotherString 交换后是 \(anotherString)")
        // someString 现在是“world”，anotherString 现在是“hello”
        
        // 使用系统的 swap
        swap(&someInt, &anotherInt)
        print("使用系统 swap ：someInt 交换后是 \(someInt), anotherInt 交换后是 \(anotherInt)")
        
        swap(&someString, &anotherString)
        print("使用系统 swap ：someString 交换后是 \(someString), anotherString 交换后是 \(anotherString)")
        
        printEndLine(title)
    }
    
    /// 2 泛型函数
    /// 函数 swapTwoInts(_:_:) 的泛型版本
    func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    /// 5 泛型类型
    func genericTypes() {
        let title = "5 泛型类型";
        printStartLine(title)
        
        // 创建一个 String 类型的栈
        var stackOfStrings = Stack<String>()
        stackOfStrings.push("uno") // 西班牙语：一
        stackOfStrings.push("dos") // 西班牙语：二
        stackOfStrings.push("tres") // 西班牙语：三
        stackOfStrings.push("cuatro") // 西班牙语：四
        // 栈中现在有 4 个字符串
        print(stackOfStrings)
        
        // 移除并返回栈顶部的值“cuatro”，即出栈：
        let fromTheTop = stackOfStrings.pop()
        // fromTheTop 的值为“cuatro”，现在栈中还有 3 个字符串
        print(fromTheTop)
        print(stackOfStrings)
        
        printEndLine(title)
    }
    
    /// 5 泛型类型
    struct IntStack {
        // 使用一个名为 items 的数组属性来存储值
        var items = [Int]()
        
        // 向栈中压入值
        // 标记为 mutating，因为需要修改结构体的 items 数组
        mutating func push(_ item: Int) {
            items.append(item)
        }
        
        // 从栈中移除值
        // 标记为 mutating，因为需要修改结构体的 items 数组
        mutating func pop() -> Int {
            return items.removeLast()
        }
    }
    
    /// 5 泛型类型
    /// 用占位类型参数 Element 代替了实际的 Int 类型
    struct Stack<Element> {
        var items = [Element]()
        mutating func push(_ item: Element) {
            items.append(item)
        }
        mutating func pop() -> Element {
            return items.removeLast()
        }
    }
    
    /// 6 泛型扩展
    func extendingAGenericType() {
        let title = "6 泛型扩展";
        printStartLine(title)
        
        var stackOfStrings = Stack<String>()
        stackOfStrings.push("uno") // 西班牙语：一
        stackOfStrings.push("dos") // 西班牙语：二
        stackOfStrings.push("tres") // 西班牙语：三
        stackOfStrings.push("cuatro") // 西班牙语：四
        
        let _ = stackOfStrings.pop()
        // fromTheTop 的值为“cuatro”，现在栈中还有 3 个字符串
        
        if let topItem = stackOfStrings.topItem {
            print("这个栈的最顶部元素是 \(topItem)")
        }
        // 这个栈的最顶部元素是 tres
        
        printEndLine(title)
    }
    
    /// 7.2 类型约束实践
    func typeConstraintsInAction() {
        let title = "7.2 类型约束实践";
        printStartLine(title)
        
        /* 使用非泛型函数findIndex(ofString:in:) 查找字符串数组中的某个字符串值 */
        let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
        if let foundIndex = findIndex(ofString: "llama", in: strings) {
            print("The index of llama is \(foundIndex)")
        }
        // 打印“The index of llama is 2”
        
        /* findIndex(of:in:) 函数适用于任何符合 Equatable 的类型，如 Double 或 String：*/
        let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
        // doubleIndex 类型为 Int?，其值为 nil，因为 9.3 不在数组中
        print(doubleIndex as Any)
        
        let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
        // stringIndex 类型为 Int?，其值为 2
        print(stringIndex as Any)
        
        printEndLine(title)
    }
    
    /// 7.2 类型约束实践
    /// 在一个 String 数组中查找给定 String 值的索引
    func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
        for (index, value) in array.enumerated() {
            if value == valueToFind {
                return index
            }
        }
        return nil
    }
    
    /// 7.2 类型约束实践
    /// 类型参数 T: Equatable意味“任何符合 Equatable 协议的类型 T”
//    func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? { // value == valueToFind 将会报错
    func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
        for (index, value) in array.enumerated() {
            // 不是所有的 Swift 类型都可以用等式符（==）进行比较
            if value == valueToFind {
                return index
            }
        }
        return nil
    }
    
    /// 8.1 关联类型实践
    func associatedTypesInAction() {
        let title = "8.1 关联类型实践";
        printStartLine(title)
        
        // 创建一个 Int 类型的栈
        var stackOfInts = IntStackV2()
        stackOfInts.push(1)
        stackOfInts.push(2)
        stackOfInts.push(3)
        stackOfInts.push(4)
        // 栈中现在有 4 个 Int
        print(stackOfInts)
        
        // 移除并返回栈顶部的值“4”，即出栈：
        let fromTheTop = stackOfInts.pop()
        // fromTheTop 的值为“4”，现在栈中还有 3 个Int
        print(fromTheTop)
        print(stackOfInts)
        
        // append
        stackOfInts.append(5)
        // count
        print(stackOfInts.count)
        // 下标访问
        print(stackOfInts[3])
        
        // 创建一个 String 类型的栈
        var stackOfStrings = StackV2<String>()
        stackOfStrings.push("uno") // 西班牙语：一
        stackOfStrings.push("dos") // 西班牙语：二
        stackOfStrings.push("tres") // 西班牙语：三
        stackOfStrings.push("cuatro") // 西班牙语：四
        // 栈中现在有 4 个字符串
        print(stackOfStrings)
        
        // 移除并返回栈顶部的值“cuatro”，即出栈：
        let fromTheTop2 = stackOfStrings.pop()
        // fromTheTop 的值为“cuatro”，现在栈中还有 3 个字符串
        print(fromTheTop2)
        print(stackOfStrings)
        
        // append
        stackOfStrings.append("五")
        // count
        print(stackOfStrings.count)
        // 下标访问
        print(stackOfStrings[3])
        
        printEndLine(title)
    }
    
    /// 8.1 关联类型实践
    /// IntStack 结构体实现了 Container 协议的三个要求，其原有功能也不会和这些要求相冲突
    struct IntStackV2: Container {
        // IntStack 的原始实现部分
        var items: [Int] = []
        mutating func push(_ item: Int) {
            items.append(item)
        }
        mutating func pop() -> Int {
            return items.removeLast()
        }
        
        // Container 协议的实现部分
        typealias Item = Int // 指定 Item 为 Int 类型，屏蔽注释也不影响（swift可以推断类型）
        mutating func append(_ item: Int) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Int {
            return items[i]
        }
    }
    
    /// 8.1 关联类型实践
    /// 让泛型 Stack 结构体遵循 Container 协议
    struct StackV2<Element>: Container {
        // Stack<Element> 的原始实现部分
        var items: [Element] = []
        mutating func push(_ item: Element) {
            items.append(item)
        }
        mutating func pop() -> Element {
            return items.removeLast()
        }
        // Container 协议的实现部分
        mutating func append(_ item: Element) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Element {
            return items[i]
        }
    }
    
    /// 8.2 扩展现有类型来指定关联类型
    func extendingAnExistingTypeToSpecifyAnAssociatedType() {
        let title = "8.2 扩展现有类型来指定关联类型";
        printStartLine(title)
        
        // 可以将任意 Array 当作 Container 来使用
        let _:[Int] = Array()
        
        printEndLine(title)
    }
    
    /// 8.4 在关联类型约束里使用协议
    func usingAProtocolInItsAssociatedTypeSConstraints() {
        let title = "8.4 在关联类型约束里使用协议";
        printStartLine(title)
        
        var stackOfInts = StackV3<Int>()
        stackOfInts.append(10)
        stackOfInts.append(20)
        stackOfInts.append(30)
        let suffix = stackOfInts.suffix(2)
        // suffix 包含 20 和 30: StackV3<Int>(items: [20, 30])
        print(suffix)
        
        /* IntStackV3 非泛型返回的类型是 StackV3 泛型类型 */
        var stackOfInts2 = IntStackV3()
        stackOfInts2.append(10)
        stackOfInts2.append(20)
        stackOfInts2.append(30)
        // suffix 包含 20 和 30: StackV3<Int>(items: [20, 30])
        print(stackOfInts2.suffix(2))
        
        printEndLine(title)
    }
    
    /// 8.4 在关联类型约束里使用协议
    struct StackV3<Element: Equatable> : ContainerV2 {
        // Stack<Element> 的原始实现部分
        var items: [Element] = []
        mutating func push(_ item: Element) {
            items.append(item)
        }
        mutating func pop() -> Element {
            return items.removeLast()
        }
        
        // Container 协议的实现部分
        mutating func append(_ item: Element) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Element {
            return items[i]
        }
    }
    
    /// 8.4 在关联类型约束里使用协议
    struct IntStackV3: ContainerV2 {
        // IntStack 的原始实现部分
        var items: [Int] = []
        mutating func push(_ item: Int) {
            items.append(item)
        }
        mutating func pop() -> Int {
            return items.removeLast()
        }
        
        // Container 协议的实现部分
        typealias Item = Int // 指定 Item 为 Int 类型，屏蔽注释也不影响（swift可以推断类型）
        mutating func append(_ item: Int) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Int {
            return items[i]
        }
    }
    
    /// 9 泛型 Where 语句
    func genericWhereClauses() {
        let title = "9 泛型 Where 语句";
        printStartLine(title)
        
        var stackOfStrings = StackV2<String>()
        stackOfStrings.push("一")
        stackOfStrings.push("二")
        stackOfStrings.push("三")
        let arrayOfStrings = ["一", "二", "三"]
        if allItemsMatch(stackOfStrings, arrayOfStrings) {
            print("都一样")
        } else {
            print("不都一样")
        }
        
        printEndLine(title)
    }
    
    /// 9 泛型 Where 语句
    func allItemsMatch<C1: Container, C2: Container>
        (_ someContainer: C1, _ anotherContainer: C2) -> Bool
        where C1.Item == C2.Item, C1.Item: Equatable {
            // 检查两个容器含有相同数量的元素
            if someContainer.count != anotherContainer.count {
                return false
            }
            // 检查每一对元素是否相等
            for i in 0..<someContainer.count {
                if someContainer[i] != anotherContainer[i] {
                    return false
                }
            }
            // 所有元素都匹配，返回 true
            return true
    }
    
    /// 10 具有泛型 Where 子句的扩展
    func extensionsWithAGenericWhereClause() {
        let title = "10 具有泛型 Where 子句的扩展";
        printStartLine(title)
        
        // 创建一个 String 类型的栈
        var stackOfStrings = Stack<String>()
        stackOfStrings.push("一")
        stackOfStrings.push("二")
        stackOfStrings.push("三")
        stackOfStrings.push("四")
        
        if stackOfStrings.isTop("三") {
            print("顶部元素是三")
        } else {
            print("顶部元素不是三")
        }
        
        /* 在其元素不符合 Equatable 协议的栈上调用 isTop(_:) 方法，则会收到编译时错误。 */
        struct NotEquatable { }
        var notEquatableStack = Stack<NotEquatable>()
        let notEquatableValue = NotEquatable()
        notEquatableStack.push(notEquatableValue)
//        notEquatableStack.isTop(notEquatableValue)  // 报错
        
        if [9, 9, 9].startsWith(42) {
            print("以 42 开头")
        } else {
            print("不是以 42 开头")
        }
        
        print([1260.0, 1200.0, 98.6, 37.0].average())
        // 打印“648.9”
        
        printEndLine(title)
    }
    
    /// 11 包含上下文关系的 Where 分句
    func contextualWhereClauses() {
        let title = "11 包含上下文关系的 Where 分句";
        printStartLine(title)
        
        /* 在一个扩展里 */
        
        // 调用 extension Container：func average() -> Double where Item == Int
        print([1260, 1200, 98, 37].average())
        // 648.75
        
        // 调用 extension Container：func endsWith(_ item: Item) -> Bool where Item: Equatable
        print([1260, 1200, 98, 37].endsWith(37))
        // true
        
        
        /* 在两个扩展 */
        
        // 调用 extension Container where Item == Int：func averageV2() -> Double
        print([1260, 1200, 98, 37].averageV2())
        // 648.75
        
        // 调用 extension Container：func endsWith(_ item: Item) -> Bool where Item: Equatable
        print([1260, 1200, 98, 37].endsWithV2(37))
        // true
        
        printEndLine(title)
    }
    
    /// 12 具有泛型 Where 子句的关联类型
    func associatedTypesWithAGenericWhereClause() {
        let title = "12 具有泛型 Where 子句的关联类型";
        printStartLine(title)
        
        // 参看 protocol ContainerV3 定义
        // 参看 protocol ComparableContainer 定义
        
        printEndLine(title)
    }
    
    /// 13 泛型下标
    func genericSubscripts() {
        let title = "13 泛型下标";
        printStartLine(title)
        
        /* 参看扩展 extension ContainerV3 中下标定义
         subscript<Indices: Sequence>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int
         */
        
        printEndLine(title)
    }
}

/// 6 泛型扩展
/// 这个扩展没有定义类型参数列表
/// Stack 类型已有的类型参数名称 Element，被用在扩展中来表示计算型属性 topItem 的可选类型
extension Generics.Stack {
    // topItem 属性返回 Element 类型的可选值
    var topItem: Element? {
        // 当栈为空的时候，topItem 会返回 nil；
        // 当栈不为空的时候，topItem 会返回 items 数组中的最后一个元素。
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

/// 8.1 关联类型实践
/// 定义一个 Container 协议，该协议定义了一个关联类型 Item
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

/// 8.2 扩展现有类型来指定关联类型
extension Array: Container {
}

/// 8.3 给关联类型添加约束
protocol ContainerV2 {
    // Item 类型必须遵守 Equatable 协议
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

/// 8.4 在关联类型约束里使用协议
/// 定一个容器需要遵循的协议：可以返回指定数量的集合中的末尾子集
protocol SuffixableContainer: ContainerV2 {
    // 拥有两个约束的关联类型：
    // 1）遵循 SuffixableContainer 自身协议
    // 2）Suffix 的 Item 类型必须是和当前容器里的 Item 类型相同
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    // 返回指定size的后缀元素集合
    func suffix(_ size: Int) -> Suffix
}

/// 8.4 在关联类型约束里使用协议
extension Generics.StackV3: SuffixableContainer where Element:Equatable {
    // 后缀运算返回另一个 Stack
    func suffix(_ size: Int) -> Generics.StackV3<Element> {
        var result = Generics.StackV3<Element>()
        for index in (count-size)..<count {
            result.append(self[index])
        }
        return result
    }
    // 推断 suffix 结果是 Generics.StackV3。
}

/// 8.4 在关联类型约束里使用协议
/// IntStack 类型扩展：后缀类型没有使用自身 IntStack，使用 Stack<Int>
extension Generics.IntStackV3: SuffixableContainer {
    func suffix(_ size: Int) -> Generics.StackV3<Int> {
        var result = Generics.StackV3<Int>()
        for index in (count-size)..<count {
            result.append(self[index])
        }
        return result
    }
    // 推断 suffix 结果是 Stack<Int>。
}

/// 10 具有泛型 Where 子句的扩展
/// 只有当栈中的元素符合 Equatable 协议时，扩展才会添加 isTop(_:) 方法。
extension Generics.Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        // 如果不声明泛型中元素遵循 Equatable 元素，直接使用 == 运算符会导致编译时错误
        return topItem == item
    }
}

/// 10 具有泛型 Where 子句的扩展
/// 泛型 where 子句要求 Item 遵循 Equatable 协议
extension Container where Item: Equatable {
    func startsWith(_ item: Item) -> Bool {
        print("调用 extension Container where Item: Equatable：func startsWith(_ item: Item) -> Bool ")
        return count >= 1 && self[0] == item
    }
}

/// 10 具有泛型 Where 子句的扩展
/// 一个泛型 where 子句要求 Item 为特定类型 Double
extension Container where Item == Double {
    // 求平均值
    func average() -> Double {
        var sum = 0.0
        for index in 0..<count {
            sum += self[index]
        }
        print("调用 extension Container where Item == Double：func average() -> Double ")
        // 将数量（count）从 Int 转换为 Double 确保能够进行浮点除法
        return sum / Double(count)
    }
}

/// 11 包含上下文关系的 Where 分句
/// 通过 where 分句对 Container 中定义的泛型 Item 进行了约束
extension Container {
    // 通过 where 声明其调用所需要满足的类型约束：
    // 当 Item 是整型时为 Container 添加 average() 方法
    func average() -> Double where Item == Int {
        var sum = 0.0
        for index in 0..<count {
            sum += Double(self[index])
        }
        print("调用 extension Container：func average() -> Double where Item == Int ")
        return sum / Double(count)
    }
    
    // 当 Item 遵循 Equatable 时添加 endsWith(_:) 方法
    func endsWith(_ item: Item) -> Bool where Item: Equatable {
        print("调用 extension Container：func endsWith(_ item: Item) -> Bool where Item: Equatable ")
        return count >= 1 && self[count-1] == item
    }
}

/// 11 包含上下文关系的 Where 分句
/// averageV2 效果等同于 extension Container：func average() -> Double where Item == Int
extension Container where Item == Int {
    func averageV2() -> Double {
        var sum = 0.0
        for index in 0..<count {
            sum += Double(self[index])
        }
        print("调用 extension Container where Item == Int：func averageV2() -> Double ")
        return sum / Double(count)
    }
}

/// 11 包含上下文关系的 Where 分句
/// endsWithV2 效果等同于 extension Container：func endsWith(_ item: Item) -> Bool where Item: Equatable
extension Container where Item: Equatable {
    func endsWithV2(_ item: Item) -> Bool {
        print("调用 extension Container where Item: Equatable：func endsWithV2(_ item: Item) -> Bool ")
        return count >= 1 && self[count-1] == item
    }
}

/// 12 具有泛型 Where 子句的关联类型
/// 一个包含迭代器（Iterator）的容器（类似标准库中 Sequence 协议）
protocol ContainerV3 {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
    // 无论迭代器是什么类型，迭代器中的元素类型，必须和容器项目的类型保持一致
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
    // 提供了容器的迭代器的访问接口
    func makeIterator() -> Iterator
}

/// 12 具有泛型 Where 子句的关联类型
/// 所有的 Item 必须是 Comparable 的
protocol ComparableContainer: ContainerV3 where Item: Comparable { }

/// 13 泛型下标
/// 添加一个下标方法，接收一个索引的集合，返回每一个索引所在的值的数组
/// 传入到 indices 下标，是一个整型的序列
extension ContainerV3 {
    // 1）尖括号中泛型参数 Indices
    // 必须是符合标准库中的 Sequence 协议的类型。（要求 indices 必须是有序的）
    // 2）indices，必须是 Indices 的实例。
    // 3）泛型 where 子句要求 Sequence（Indices）的迭代器，其所有的元素都是 Int 类型。
    // （确保在序列（Sequence）中索引和容器（Container）中索引类型一致
    subscript<Indices: Sequence>(indices: Indices) -> [Item]
    where Indices.Iterator.Element == Int {
        var result: [Item] = []
        for index in indices {
            result.append(self[index])
        }
        return result
    }
}
